//    Copyright (C) Kherty.  All rights reserved.

using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using OpenLS.Core;
using OpenLS.Drawing.UI;
using OpenLS.Spreadsheet.Internals;
using OpenLS.Spreadsheet.UI.Internals;
using OpenLS.Spreadsheet.UI.Internals.Resources;
using OpenLS.UI;
using FormatCategory = OpenLS.Core.FormatCategory;

namespace OpenLS.Spreadsheet.UI
{
    internal class GridEditing
    {
        internal static void EnableCommands(UIElement g)
        {
            g.CommandBindings.Add(new CommandBinding(SpreadsheetCommands.Fill, onFill, WorksheetHelper.OnAlways));
            g.InputBindings.Add(new InputBinding(SpreadsheetEditingCommands.SelectAll,
                                                 new KeyGesture(Key.A, ModifierKeys.Control)));
            g.CommandBindings.Add(new CommandBinding(SpreadsheetEditingCommands.ToggleEditionMode, onToggleEditionMode,
                                                     WorksheetHelper.OnAlways));
            g.InputBindings.Add(new InputBinding(SpreadsheetEditingCommands.ToggleEditionMode, new KeyGesture(Key.F2)));
            g.InputBindings.Add(new InputBinding(SpreadsheetCommands.SwitchWindow,
                                                 new KeyGesture(Key.Tab, ModifierKeys.Control)));
            g.InputBindings.Add(new InputBinding(SpreadsheetCommands.SwitchWindow,
                                                 new KeyGesture(Key.Tab, ModifierKeys.Control)));
            AddWorksheetCommand(g, SpreadsheetCommands.DefineName, onDefineName,
                                WorksheetHelper.DefaultProtectionFunction);
            AddWorksheetCommand(g, SpreadsheetCommands.FormatCells, onFormatCells,
                                WorksheetHelper.FormatCellProtectionFunction);
            g.CommandBindings.Add(new CommandBinding(EditingCommands.Delete, onClear, WorksheetHelper.OnHasSelection));
            g.InputBindings.Add(new InputBinding(EditingCommands.Delete, new KeyGesture(Key.Delete)));
            g.CommandBindings.Add(new CommandBinding(SpreadsheetCommands.Escape, onEscape,
                                                     WorksheetHelper.OnHasSelection));
            g.InputBindings.Add(new InputBinding(SpreadsheetCommands.Escape, new KeyGesture(Key.Escape)));
            g.TextInput += onTextInput;
            g.MouseDown += onMouseDown;
            //\\ TODO: EventManager.RegisterClassHandler(typeof(Grid), Grid.TextInputEvent, new TextCompositionEventHandler(OnText));

            g.CommandBindings.Add(new CommandBinding(SpreadsheetEditingCommands.MergeCells, onMergeCells,
                                                     OnHasMergeableSelection));
            AddWorksheetCommand(g, SpreadsheetEditingCommands.UnmergeCells, onSplitCells,
                                WorksheetHelper.DefaultProtectionFunction);
            g.CommandBindings.Add(new CommandBinding(SpreadsheetEditingCommands.MergeCellsAcross, onMergeCellsAcross,
                                                     OnHasMergeableSelection));
            g.CommandBindings.Add(new CommandBinding(SpreadsheetEditingCommands.MergeCellsAndCenter,
                                                     onMergeCellsAndCenter, OnHasMergeableSelection));

            AddWorksheetCommand(g, SpreadsheetCommands.HideRows, onHideRows, always2);
            AddWorksheetCommand(g, SpreadsheetCommands.UnhideRows, onUnhideRows, always2);

            AddWorksheetCommand(g, SpreadsheetCommands.HideColumns, onHideColumns, always2);
            AddWorksheetCommand(g, SpreadsheetCommands.UnhideColumns, onUnhideColumns, always2);
            AddWorksheetCommand(g, SpreadsheetCommands.DeleteColumns, onDeleteColumns, onHasDeletableColumn); //\\??
            AddWorksheetCommand(g, SpreadsheetCommands.DeleteRows, onDeleteRows, onHasDeletableRow); //\\??
            AddWorksheetCommand(g, SpreadsheetCommands.InsertRow, onInsertRow,
                                delegate(Worksheet worksheet)
                                    {
                                        return !worksheet.IsProtected(p => p.InsertRows) &&
                                               (worksheet.Selection is FullRowRange || worksheet.Selection is CellsRange);
                                    });
            AddWorksheetCommand(g, SpreadsheetCommands.InsertColumn, onInsertColumn,
                                delegate(Worksheet worksheet)
                                    {
                                        return !worksheet.IsProtected(p => p.InsertColumns) &&
                                               (worksheet.Selection is FullColumnRange ||
                                                worksheet.Selection is CellsRange);
                                    });

            AddWorksheetCommand(g, SpreadsheetCommands.InsertCells, onInsertCells,
                                WorksheetHelper.DefaultProtectionFunction);
            AddWorksheetCommand(g, SpreadsheetCommands.DeleteCells, onDeleteCells,
                                WorksheetHelper.DefaultProtectionFunction);

            GridCommandHelper.AddRepeatableWorksheetCommandBinding(g, SpreadsheetEditingCommands.SetFormat, onSetFormat);
            AddWorksheetCommand(g, SpreadsheetCommands.IncreaseDecimal, onIncreaseDecimal,
                                WorksheetHelper.FormatCellProtectionFunction);
            AddWorksheetCommand(g, SpreadsheetCommands.DecreaseDecimal, onDecreaseDecimal,
                                WorksheetHelper.FormatCellProtectionFunction);

            g.CommandBindings.Add(new CommandBinding(SpreadsheetCommands.SortUp, OnSortUp, OnCanSort));
            g.CommandBindings.Add(new CommandBinding(SpreadsheetCommands.SortDown, OnSortDown, OnCanSort));
            g.CommandBindings.Add(new CommandBinding(SpreadsheetCommands.CustomSort, OnCustomSort, OnCanSort));
            g.MouseWheel += OnMouseWheel;
            g.CommandBindings.Add(new CommandBinding(SpreadsheetCommands.SelectRow, onSelectRow,
                                                     WorksheetHelper.OnHasSelection));
            g.CommandBindings.Add(new CommandBinding(SpreadsheetCommands.SelectColumn, onSelectColumn,
                                                     WorksheetHelper.OnHasSelection));
            var commandBinding = new CommandBinding(ApplicationCommands.Save);
            commandBinding.PreviewExecuted += onWillSave;
            g.CommandBindings.Add(commandBinding);
            commandBinding = new CommandBinding(ApplicationCommands.SaveAs);
            commandBinding.PreviewExecuted += onWillSave;
            g.CommandBindings.Add(commandBinding);
            g.InputBindings.Add(new InputBinding(SpreadsheetCommands.InsertCurrentDate,
                                                 new KeyGesture(Key.OemSemicolon, ModifierKeys.Control)));
            g.InputBindings.Add(new InputBinding(SpreadsheetCommands.InsertCurrentTime,
                                                 new KeyGesture(Key.OemSemicolon,
                                                                ModifierKeys.Control | ModifierKeys.Shift)));
            AddWorksheetCommand(g, SpreadsheetCommands.InsertCurrentDate, onInsertCurrentDate,
                                WorksheetHelper.OnHasSelection2);
            AddWorksheetCommand(g, SpreadsheetCommands.InsertCurrentTime, onInsertCurrentTime,
                                WorksheetHelper.OnHasSelection2);
        }


        private static void AddWorksheetCommand(UIElement g, ICommand command, Action<Worksheet> onExecute,
                                                Func<Worksheet, bool> onCanExecute)
        {
            g.CommandBindings.Add(new CommandBinding(command, (sender, args) =>
                                                                  {
                                                                      onExecute
                                                                          (getWorksheet
                                                                               (sender));
                                                                      args.Handled = true;
                                                                  },
                                                     (sender, args) =>
                                                         {
                                                             args.CanExecute = onCanExecute(getWorksheet(sender));
                                                             args.Handled = true;
                                                         }));
        }

        private static void AddWorksheetCommand(UIElement g, ICommand command, ExecutedRoutedEventHandler onExecute,
                                                Func<Worksheet, bool> protectionFunction)
        {
            g.CommandBindings.Add(new CommandBinding(command, onExecute,
                                                     (sender, args) =>
                                                         {
                                                             args.CanExecute = protectionFunction(getWorksheet(sender));
                                                             args.Handled = true;
                                                         }));
        }

        private static void onInsertCurrentDate(Worksheet worksheet)
        {
            worksheet.ActiveCell.Value = DateTime.Today;
            if (worksheet.ActiveCell.Format == null || (worksheet.ActiveCell.Format.Category != FormatCategory.Date))
                worksheet.ActiveCell.Format = Core.Formats.ShortDate;
        }

        private static void onInsertCurrentTime(Worksheet ws)
        {
            ws.ActiveCell.Value = ws.Workbook.DoubleToDateTime(0) + DateTime.Now.TimeOfDay;
            if (ws.ActiveCell.Format == null || (ws.ActiveCell.Format.Category != FormatCategory.Time))
                ws.ActiveCell.Format = Core.Formats.Time;
        }

        private static void onWillSave(object sender, ExecutedRoutedEventArgs args)
        {
            UIElement g = (WorksheetGrid) sender;
            StopEditing(g, true);
        }

        private static void onSelectRow(object sender, ExecutedRoutedEventArgs args)
        {
            Contract.Assert(EditionElement.GetEditedCell((DependencyObject) sender) == null);
            Worksheet ws = getWorksheet(sender);
            ws.Selection = ws.Selection.ToFullRowRange();
            args.Handled = true;
        }

        private static void onSelectColumn(object sender, ExecutedRoutedEventArgs args)
        {
            Worksheet ws = getWorksheet(sender);
            ws.Selection = ws.Selection.ToFullColumnRange();
            args.Handled = true;
        }

        internal static void OnMouseWheel(object sender, MouseWheelEventArgs e)
        {
            Worksheet ws = getWorksheet(sender);
            if (ws == null)
                return;
            if ((Keyboard.Modifiers & ModifierKeys.Control) != 0)
            {
                OnZoom(sender, e);
                return;
            }
            WorksheetPane pane = ws.BottomLeftPane ?? ws.TopLeftPane;
            int top = pane.Top;
            if (e.Delta < 0)
                top += SystemParameters.WheelScrollLines;
            else
            {
                top -= SystemParameters.WheelScrollLines;
                int min = pane.MinTop;
                if (top < min)
                    top = min;
            }
            pane.Top = top;
            e.Handled = true;
        }

        private static void OnZoom(object sender, MouseWheelEventArgs mouseWheelEventArgs)
        {
            Worksheet ws = getWorksheet(sender);
            ws.View.ZoomScale = GetZoom(ws.View.ZoomScale, mouseWheelEventArgs.Delta.CompareTo(0));
            var ds = (DependencyObject) sender;

            WorksheetPane pane = SpreadsheetElement.GetWorksheetPane(ds);
            if (sender is WorksheetGrid && ws.ActiveCell != null && pane != null)
            {
                pane.EnsureVisible(ws.ActiveCell);
            }
            mouseWheelEventArgs.Handled = true;
        }

        private static int GetZoom(int zoomScale, int up)
        {
            int result = (zoomScale - 10)/15;
            result += up;
            result = 10 + 15*result;
            result = Math.Max(Math.Min(result, 400), 10);
            return result;
        }

        private static void onMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ClickCount > 1)
            {
                Worksheet ws = getWorksheet(sender);
                if (!ws.ActiveCell.CanBeEdited)
                {
                    SpreadsheetElement.OnProtectionViolation(sender as UIElement);
                    return;
                }
                EditionElement.SetEditedCellFor(sender,
                                                new EditedCell(
                                                    SpreadsheetElement.GetWorksheetViewModel((DependencyObject) sender),
                                                    ws.ActiveCell, GetActiveText(ws)));
                focusEditor(sender);

                //\\EditActive(txt, ws, sender as WorksheetGrid);
            }
        }

        private static void focusEditor(object sender)
        {
            var frameworkElement = (FrameworkElement) sender;
            var contentControl = (ContentControl) frameworkElement.Tag;
            //\\ c.Visibility = Visibility.Visible;
            int i = VisualTreeHelper.GetChildrenCount(contentControl);
            if (i == 0)
            {
                return;
            }
            var contentPresenter = (ContentPresenter) VisualTreeHelper.GetChild(contentControl, 0);
        }

        internal static string GetActiveText(Worksheet ws)
        {
            string result = null;
            string s = ws.ActiveCell.LocalizedFormula;
            if (s != null)
                result = s;
            else if (ws.ActiveCell.Value != null)
                result = ws.ActiveCell.FormulaFormattedValue;
            return result;
        }

        internal static void StopEditing(object sender, bool commit)
        {
            if (sender is TextBox)
            {
                var editor = (TextBox) sender;
                var worksheetGrid = (WorksheetGrid) editor.Parent;
                Worksheet ws = getWorksheet(editor.Parent);
                string oldFormula = ws.ActiveCell.Formula;
                object oldValue = ws.ActiveCell.Value;
                EditionElement.SetEditedCellFor(sender, null); //\\ ws.EditedCell = null;
                if (commit)
                {
                    try
                    {
                        ws.ActiveCell.Formula = editor.Text;
                    }
                    catch (CircularReferenceException)
                    {
                        MessageBox.Show(UIStringTable.CircularReference);
                        ws.ActiveCell.Value = oldValue;
                        ws.ActiveCell.Formula = oldFormula;
                    }
                }
                Debug.WriteLine("Removing editor " + editor);
                worksheetGrid.Children.Remove(editor);
                worksheetGrid.Focus();
                SpreadsheetElement.ForgetHightlightForAndClipboardMonitor(editor);
                return;
            }
            if (sender is WorksheetGrid)
            {
                var worksheetGrid = (WorksheetGrid) sender;

                for (int i = 0; i < worksheetGrid.Children.Count; i++)
                {
                    if (worksheetGrid.Children[i] is TextBox)
                    {
                        Debug.WriteLine("Removing TextBox child");
                        worksheetGrid.Children.RemoveAt(i);
                        i--;
                    }
                }
            }
        }

        private static void onTextInput(object sender, TextCompositionEventArgs e)
        {
            editActiveCell(sender, e.Text, EditionMode.Enter);
        }

        private static void editActiveCell(object sender, string text, EditionMode mode)
        {
            var dObject = (DependencyObject) sender;
            Worksheet worksheet = getWorksheet(dObject);
            WorksheetViewModel worksheetViewModel = getworksheetViewModel(dObject);
            if (worksheet == null)
                return;
            if (worksheet.ActiveCell == null)
                return;
            if (!worksheet.ActiveCell.CanBeEdited)
            {
                SpreadsheetElement.OnProtectionViolation(sender as UIElement);
                return;
            }
            EditedCell editedCell = EditionElement.GetEditedCell((DependencyObject) sender);
            if (editedCell != null)
            {
                editedCell.Text += text;
                editedCell.Formula.updateHotReference(editedCell.Text.Length, editedCell.Text.Length);
                return;
            }
            SpreadsheetElement.ForgetHightlightForAndClipboardMonitor(sender);
            EditionElement.SetEditedCellFor(sender, new EditedCell(worksheetViewModel, worksheet.ActiveCell, text, mode));
            //Worksheet.EditActive(e.Text);
            focusEditor(sender);
        }

        private static WorksheetViewModel getworksheetViewModel(DependencyObject sender)
        {
            return WorksheetHelper.GetWorksheetViewModel(sender);
        }

        private static void onToggleEditionMode(object sender, ExecutedRoutedEventArgs args)
        {
            Worksheet ws = SpreadsheetElement.GetWorksheet(sender);
            EditedCell cell = EditionElement.GetEditedCell((DependencyObject) sender);
            if (cell != null)
            {
                cell.ToggleEditionMode();
                return;
            }
            if (ws == null)
                return;
            if (!ws.ActiveCell.CanBeEdited)
            {
                SpreadsheetElement.OnProtectionViolation(sender as UIElement);
                args.Handled = true;
                return;
            }
            editActiveCell(sender, GetActiveText(ws), EditionMode.Edit);

            args.Handled = true;
        }


        private static void stopEditingAndMove(object sender, bool commit, RoutedUICommand routedUICommand)
        {
            UIElement grid = ((FrameworkElement) sender).Parent as WorksheetGrid;
            StopEditing(sender, commit);
            routedUICommand.Execute(null, grid);
        }

        private static Worksheet getWorksheet(object sender)
        {
            return WorksheetHelper.GetWorksheet(sender);
        }

        private static void onIncreaseDecimal(Worksheet worksheet)
        {
            //Worksheet worksheet = getWorksheet(sender);
            if (worksheet.Selection == null)
                return;
            worksheet.Selection.IncreaseDecimal();
        }

        

        private static void onDecreaseDecimal(Worksheet worksheet)
        {
            if (worksheet.Selection == null)
                return;
            worksheet.Selection.DecreaseDecimal();
        }

        private static void onSetFormat(Worksheet worksheet, object parameter)
        {
            var fmt = parameter as Format;
            worksheet.Selection.SetCellValue(CellProperty.Format, fmt);
        }

        internal static void OnCanSort(object sender, CanExecuteRoutedEventArgs args)
        {
            args.Handled = true;
            var range = args.Parameter as Range;
            Worksheet Worksheet = getWorksheet(sender);
            if (Worksheet == null || Worksheet.IsProtected(p => p.Sort))
                return;
            if (range != null)
            {
                args.CanExecute = true;
                return;
            }
            args.CanExecute = Worksheet.Selection != null;
        }

        private static void OnSortUp(object sender, ExecutedRoutedEventArgs args)
        {
            OnSortUpOrDown(sender, args, CellComparator.CompareCellsUp);
        }

        private static void OnSortDown(object sender, ExecutedRoutedEventArgs args)
        {
            OnSortUpOrDown(sender, args, CellComparator.CompareCellsDown);
        }

        private static void OnSortUpOrDown(object sender, ExecutedRoutedEventArgs args, Comparison<Cell> comparison)
        {
            Range range;
            int index = 0;
            if (args.Parameter is TableColumn)
            {
                var tableColumn = (TableColumn) args.Parameter;
                range = tableColumn.Table.RangeWithoutHeader;
                index = tableColumn.Column.Index - range.TopLeftCell.Column.Index;
            }
            else if (args.Parameter is FilterColumn)
            {
                var filterColumn = (FilterColumn) args.Parameter;
                range = filterColumn.RangeWithoutHeader;
                index = filterColumn.Column.Index - filterColumn.Range.TopLeftCell.Column.Index;
            }
            else if (args.Parameter is Range)
            {
                range = (Range) args.Parameter;
            }
            else
                range = SpreadsheetElement.GetWorksheet(sender).Selection;
            if (range != null)
                onSort(getWorksheet(sender),
                       (r1, r2) => comparison(r1.Cells[index], r2.Cells[index]),
                       RowOrColumn.Row, range);
            args.Handled = true;
        }

        private static void OnCustomSort(object sender, ExecutedRoutedEventArgs args)
        {
            Worksheet Worksheet = getWorksheet(sender);
            Range selection = Worksheet.Selection;
            if (selection == null)
                return;
            var sortState = new SortState(selection);
            var sortCondition = new SortCondition(sortState) {Index = 0};
            sortState.SortConditions.Add(sortCondition);
            var customSortControl = new CustomSortControl {DataContext = sortState};
            WindowServices.ShowDialog(
                customSortControl, UIStringTable.Sort, sender as UIElement,
                delegate
                    {
                        Worksheet.Selection.Sort(
                            sortState.Compare,
                            sortState.SortByColumn
                                ? RowOrColumn.Column
                                : RowOrColumn.Row);
                    }, o=>false);
            args.Handled = true;
        }

        private static void onSort(Worksheet worksheet, Comparison<PartialRowOrColumnRange> comparison, RowOrColumn rc,
                                   Range rangeToSort)
        {
            Range r = rangeToSort ?? worksheet.Selection;
            if (r == null)
                return;
            foreach (Table table in worksheet.Tables)
            {
                if (Range.Intersects(table.Range, r))
                {
                    r = table.RangeWithoutHeader;
                }
            }
            r.Sort(comparison, rc);
        }

        private static void onInsertCells(object sender, ExecutedRoutedEventArgs args)
        {
            Worksheet worksheet = getWorksheet(sender);
            args.Handled = true;
            if (worksheet.Selection == null)
                return;
            using (new StableScope(worksheet))
            {
                if (worksheet.Selection is FullColumnRange)
                {
                    var fullColumnRange = (FullColumnRange) worksheet.Selection;
                    worksheet.InsertColumnRange(fullColumnRange.Column,
                                                fullColumnRange.Column + fullColumnRange.ColumnSpan - 1);
                    return;
                }
                if (worksheet.Selection is FullRowRange)
                {
                    var fullRowRange = (FullRowRange) worksheet.Selection;
                    worksheet.InsertRowRange(fullRowRange.Row, fullRowRange.Row + fullRowRange.RowSpan - 1);
                    return;
                }
                var cellsRange = worksheet.Selection as CellsRange;
                if (args.Parameter != null && cellsRange != null)
                {
                    worksheet.InsertCellsRange(cellsRange.Address, (CellMoveOrientationHint) args.Parameter, cellsRange);
                    return;
                }
                var insertRangeControl = new InsertRangeControl();
                WindowServices.ShowDialog(
                    insertRangeControl, OpenLS.UI.Internals.UIStringTable.Insert, sender as UIElement,
                    delegate
                        {
                            if (cellsRange != null)
                            {
                                switch (insertRangeControl.Type)
                                {
                                    case RangeInsertionType.EntireRow:
                                        worksheet.InsertRowRange(cellsRange.ToFullRowRange().Address);
                                        break;

                                    case RangeInsertionType.EntireColumn:
                                        worksheet.InsertColumnRange(cellsRange.ToFullColumnRange().Address);
                                        break;

                                    case RangeInsertionType.ShiftCellsDown:
                                        worksheet.InsertCellsRange(cellsRange.Address, CellMoveOrientationHint.Vertical,
                                                                   cellsRange);
                                        break;

                                    case RangeInsertionType.ShiftCellsRight:
                                        worksheet.InsertCellsRange(cellsRange.Address,
                                                                   CellMoveOrientationHint.Horizontal, cellsRange);
                                        break;
                                    default:
                                        throw new NotSupportedException();
                                }
                            }
                        }, o => false
                    );
            }
        }

        private static void onDeleteCells(object sender, ExecutedRoutedEventArgs args)
        {
            Worksheet worksheet = getWorksheet(sender);
            args.Handled = true;
            if (args.Parameter != null && args.Parameter is CellMoveOrientationHint)
            {
                worksheet.DeleteRange(worksheet.Selection, (CellMoveOrientationHint) args.Parameter);
                return;
            }
            if (worksheet.Selection is CellsRange)
            {
                var deleteRangeControl = new DeleteRangeControl();
                var cellsRange = (CellsRange) worksheet.Selection;
                WindowServices.ShowDialog(deleteRangeControl, UIStringTable.Delete, sender as UIElement,
                                          delegate
                                              {
                                                  switch (deleteRangeControl.Type)
                                                  {
                                                      case RangeDeletionType.EntireRow:
                                                          worksheet.DeleteFullRange(cellsRange.ToFullRowRange());
                                                          break;
                                                      case RangeDeletionType.EntireColumn:
                                                          worksheet.DeleteFullRange(cellsRange.ToFullColumnRange());
                                                          break;

                                                      case RangeDeletionType.ShiftCellsLeft:
                                                          worksheet.DeleteRange(cellsRange,
                                                                                CellMoveOrientationHint.Horizontal);
                                                          break;

                                                      case RangeDeletionType.ShiftCellsUp:
                                                          worksheet.DeleteRange(cellsRange,
                                                                                CellMoveOrientationHint.Vertical);
                                                          break;
                                                      default:
                                                          throw new NotSupportedException();
                                                  }
                                              },  o => false);
                return;
            }
            if (worksheet.Selection is FullRange)
                worksheet.DeleteFullRange((FullRange) worksheet.Selection);
            throw new NotSupportedException();
        }

        private static void onInsertColumn(Worksheet worksheet)
        {
            Range range = worksheet.Selection;
            if (range == null)
                return;
            if (range is CellsRange)
                range = ((CellsRange) range).ToFullColumnRange();
            if (range is FullColumnRange)
            {
                var fullColumnRange = (FullColumnRange) range;
                using (new StableScope(worksheet))
                {
                    worksheet.InsertColumnRange(fullColumnRange.FirstColumn.Index, fullColumnRange.LastColumn.Index);
                }
            }
        }

        private static void onInsertRow(Worksheet worksheet)
        {
            Range range = worksheet.Selection;
            if (range == null)
                return;
            if (range is CellsRange)
                range = ((CellsRange) range).ToFullRowRange();
            if (range is FullRowRange)
            {
                var fullRowRange = (FullRowRange) range;
                using (new StableScope(worksheet))
                {
                    worksheet.InsertRowRange(fullRowRange.FirstRow.Index, fullRowRange.LastRow.Index);
                }
            }
        }

        private static void always(object sender, CanExecuteRoutedEventArgs args)
        {
            args.CanExecute = true;
        }

        private static bool always2(Worksheet worksheet)
        {
            return true;
        }

        private static void onDeleteRows(Worksheet worksheet)
        {
            if (worksheet.Selection == null)
                return;
            Range r = worksheet.Selection;
            if (r is FullRowRange)
            {
                var fullRowRange = (FullRowRange) r;
                worksheet.DeleteFullRange(fullRowRange);
                worksheet.Selection = new FullRowRange(worksheet.Rows[fullRowRange.FirstRow.Index]);

                return;
            }
            if (r is CellsRange)
            {
                var cellsRange = (CellsRange) r;
                RangeAddress address = cellsRange.Address;
                worksheet.DeleteFullRange(cellsRange.ToFullRowRange());
                worksheet.Selection = CellsRange.Create(worksheet, address);
                return;
            }
        }

        private static void onHideColumns(Worksheet worksheet)
        {
            hideOnUnhideColumns(worksheet, false);
        }

        private static void onUnhideColumns(Worksheet worksheet)
        {
            hideOnUnhideColumns(worksheet, true);
        }

        private static void hideOnUnhideColumns(Worksheet ws, bool value)
        {
            if (ws.Selection != null)
                using (ws.Workbook.CreateUndoScope(StringTable.ColumnWidth))
                {
                    foreach (Column column in ws.Selection.GetColumns())
                    {
                        column.IsVisible = value;
                        if (value && column.CharacterWidth.GetValueOrDefault(0) == 0)

                            column.CharacterWidth = null;
                    }
                }
        }

        private static void hideOnUnhideColumns(object sender, RoutedEventArgs args, bool value)
        {
            Worksheet ws = getWorksheet(sender);
            hideOnUnhideColumns(ws, value);
            args.Handled = true;
        }

        private static void onHideRows(object sender, ExecutedRoutedEventArgs args)
        {
            hideOnUnhideRows(sender, args, false);
        }

        private static void onUnhideRows(object sender, ExecutedRoutedEventArgs args)
        {
            hideOnUnhideRows(sender, args, true);
        }

        private static void hideOnUnhideRows(object sender, RoutedEventArgs args, bool value)
        {
            Worksheet ws = getWorksheet(sender);
            if (ws.Selection != null)
                using (ws.Workbook.CreateUndoScope(StringTable.RowHeight))
                {
                    foreach (Row row in ws.Selection.GetRows())
                    {
                        row.IsVisible = value;
                        if (value && row.Height.GetValueOrDefault(0) == 0)
                            row.Height = null;
                    }
                }
            args.Handled = true;
        }

        private static void onDeleteColumns(Worksheet worksheet)
        {
            Range r = worksheet.Selection;
            FullColumnRange toDelete = null;
            if (r is FullColumnRange)
                toDelete = (FullColumnRange) r;
            if (r is CellsRange)
                toDelete = ((CellsRange) r).ToFullColumnRange();
            if (toDelete != null)
            {
                using (new StableScope(worksheet))
                {
                    worksheet.DeleteFullRange(toDelete);
                }
            }
        }

        private static bool onHasDeletableRow(Worksheet worksheet)
        {
            if (worksheet == null || worksheet.Selection == null || worksheet.IsProtected(p => p.DeleteRows))
                return false;
            Range range = worksheet.Selection;
            if (range != null)
            {
                if (range is CellsRange)
                    return true;
                if (range is FullRowRange)
                    return true;
            }
            return false;
        }

        private static bool onHasDeletableColumn(Worksheet worksheet)
        {
            if (worksheet == null || worksheet.Selection == null || worksheet.IsProtected(p => p.DeleteColumns))
                return false;
            Range range = worksheet.Selection;
            if (range == null)
                return false;
            {
                if (range is CellsRange)
                    return true;
                if (range is FullColumnRange)
                    return true;
            }
            return false;
        }

        private static void onSplitCells(Worksheet worksheet)
        {
            worksheet.Unmerge(worksheet.Selection);
        }

        internal static void OnHasMergeableSelection(object sender, CanExecuteRoutedEventArgs args)
        {
            args.Handled = true;
            Worksheet worksheet = getWorksheet(sender);
            if (worksheet == null)
                return;
            Range selection = worksheet.Selection;
            if (selection == null)
            {
                return;
            }
            foreach (Table table in worksheet.Tables)
                if (Range.Intersects(table.Range, selection))
                    return;
            args.CanExecute = true;
        }

        private static void onMergeCellsAndCenter(object sender, ExecutedRoutedEventArgs args)
        {
            Worksheet worksheet = getWorksheet(sender);
            using (worksheet.CreateUndoScope(UIStringTable.Merge))
            {
                worksheet.Merge(worksheet.Selection);
                worksheet.Selection.SetCellValue(CellProperty.HorizontalAlignment, HorizontalCellAlignment.Center);
            }
        }

        private static void onMergeCellsAcross(object sender, ExecutedRoutedEventArgs args)
        {
            Worksheet worksheet = getWorksheet(sender);
            worksheet.MergeAcross(worksheet.Selection);
        }

        private static void onMergeCells(object sender, ExecutedRoutedEventArgs args)
        {
            Worksheet worksheet = getWorksheet(sender);
            worksheet.Merge(worksheet.Selection);
        }

        private static void onClear(object sender, ExecutedRoutedEventArgs args)
        {
            //\\Grid This = (Grid)sender;
            if (sender != args.Source)
                return;
            if (sender != Keyboard.FocusedElement)
                return;
            SpreadsheetElement.ForgetHightlightForAndClipboardMonitor(sender);

            Worksheet worksheet = getWorksheet(sender);
            if (!worksheet.IsProtected())
                worksheet.Selection.ClearValues();
            args.Handled = true;
        }

        private static void onFill(object sender, ExecutedRoutedEventArgs args)
        {
            Worksheet worksheet = getWorksheet(sender);
            var cellsRange = worksheet.Selection as CellsRange;
            if (cellsRange == null)
                return;
            var navigationDirection = (NavigationDirection) args.Parameter;
            CellsRange sliceRange = CellsRangeOperations.GetSlice(cellsRange,
                                                                  CellsRangeOperations.GetOpposite(navigationDirection));
            var autoFill = new AutoFill(sliceRange, sender as UIElement);
            int length;
            switch (navigationDirection)
            {
                case NavigationDirection.Down:
                case NavigationDirection.Up:
                    length = cellsRange.RowSpan;
                    break;
                case NavigationDirection.Left:
                case NavigationDirection.Right:
                    length = cellsRange.ColumnSpan;
                    break;
                default:
                    throw new NotSupportedException();
            }
            length = length - 1;
            if (length == 0)
                return;
            autoFill.SetTarget(navigationDirection, length);
            autoFill.Apply();
            args.Handled = true;
        }

        private static void onDefineName(object sender, ExecutedRoutedEventArgs args)
        {
            Worksheet worksheet = getWorksheet(sender);
            var newDefinedNameControl = new NewDefinedNameControl();
            var definedName = new DefinedName(worksheet.Workbook) {Formula = worksheet.Selection.FullLabel};
            newDefinedNameControl.DataContext = definedName;
            WindowServices.ShowDialog(
                newDefinedNameControl, UIStringTable.NewName,
                sender as UIElement,
                delegate
                    {
                        if (worksheet.Workbook.IsValidName(definedName.Name, worksheet))
                            worksheet.Workbook.AddDefinedName(definedName);
                    }, o=>false
                );

            args.Handled = true;
        }

        private static void onFormatCells(object sender, ExecutedRoutedEventArgs args)
        {
            var styleRecordControl = new StyleRecordControl();
            var frameworkElement = (FrameworkElement) sender;

            //w.Owner = WindowHelper.GetAncestor<Window>(sender);
            Worksheet worksheet = getWorksheet(sender);
            if (worksheet.Selection == null)
                return;
            if (worksheet.Workbook.Theme != null)
                DrawingElement.SetColorScheme(styleRecordControl, worksheet.Workbook.Theme.ColorScheme);
            CellFormattingRecord sr = worksheet.Selection.GetFormattingRecord() ?? new CellFormattingRecord();
            styleRecordControl.DataContext = new RangeFormatter(sr.Clone(), worksheet.Selection);
            SpreadsheetElement.SetWorkbook(styleRecordControl, worksheet.Workbook);
            DrawingElement.SetTheme(styleRecordControl, worksheet.Workbook.Theme);
            SpreadsheetElement.SetWorkbook(styleRecordControl, worksheet.Workbook);
            var s = args.Parameter as string;
            switch (s)
            {
                case "Font":
                    styleRecordControl.FontTabItem.IsSelected = true;
                    break;
                case "Alignment":
                    styleRecordControl.AlignmentTabItem.IsSelected = true;
                    break;
                case "Borders":
                    styleRecordControl.BordersTabItem.IsSelected = true;
                    break;
            }
            WindowServices.ShowDialog(
                styleRecordControl, UIStringTable.FormatCell,
                frameworkElement, delegate
                                      {
                                          var formatter = (RangeFormatter) styleRecordControl.DataContext;
                                          formatter.Apply(worksheet.Selection);
                                      },  o => false);
            args.Handled = true;
        }

        private static void onEscape(object sender, ExecutedRoutedEventArgs args)
        {
            SpreadsheetElement.ForgetHightlightForAndClipboardMonitor(sender);
            EditionElement.SetEditedCellFor(sender, null);
            args.Handled = true;
        }

        #region Nested type: StableScope

        private class StableScope : IDisposable
        {
            private readonly Worksheet _worksheet;
            private CellAddress? _active;
            private RangeAddress? _selection;

            public StableScope(Worksheet ws)
            {
                _worksheet = ws;
                if (ws.ActiveCell != null)
                    _active = ws.ActiveCell.Address;
                if (ws.Selection != null)
                    _selection = ws.Selection.Address;
            }

            #region IDisposable Members

            public void Dispose()
            {
                if (_active != null)
                    _worksheet.ActiveCell = _worksheet[_active.Value];
                if (_selection != null)
                    _worksheet.Selection = _worksheet.GetRange(_selection.Value);
                //\\worksheet.Selection = Range.FromAddress(worksheet, selection.Value);
            }

            #endregion
        }

        #endregion
    }
}